package com.yuehuanghun.framework.util;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;

/**
 * ID生成器工具类
 * 
 * @author yadmin
 */
public class IdUtils {
	/**
	 * 获取随机UUID
	 * 
	 * @return 随机UUID
	 */
	public static String randomUUID() {
		return UUID.randomUUID().toString();
	}

	/**
	 * 简化的UUID，去掉了横线
	 * 
	 * @return 简化的UUID，去掉了横线
	 */
	public static String simpleUUID() {
		return UUID.randomUUID().toString(true);
	}

	/**
	 * 获取随机UUID，使用性能更好的ThreadLocalRandom生成UUID
	 * 
	 * @return 随机UUID
	 */
	public static String fastUUID() {
		return UUID.fastUUID().toString();
	}

	/**
	 * 简化的UUID，去掉了横线，使用性能更好的ThreadLocalRandom生成UUID
	 * 
	 * @return 简化的UUID，去掉了横线
	 */
	public static String fastSimpleUUID() {
		return UUID.fastUUID().toString(true);
	}

	/**
	 * 提供通用唯一识别码（universally unique identifier）（UUID）实现
	 *
	 */
	private static final class UUID implements java.io.Serializable, Comparable<UUID> {
		private static final long serialVersionUID = -1185015143654744140L;

		/**
		 * SecureRandom 的单例
		 *
		 */
		private static class Holder {
			static final SecureRandom numberGenerator = getSecureRandom();
		}

		/** 此UUID的最高64有效位 */
		private final long mostSigBits;

		/** 此UUID的最低64有效位 */
		private final long leastSigBits;

		/**
		 * 私有构造
		 * 
		 * @param data 数据
		 */
		private UUID(byte[] data) {
			long msb = 0;
			long lsb = 0;
			assert data.length == 16 : "data must be 16 bytes in length";
			for (int i = 0; i < 8; i++) {
				msb = (msb << 8) | (data[i] & 0xff);
			}
			for (int i = 8; i < 16; i++) {
				lsb = (lsb << 8) | (data[i] & 0xff);
			}
			this.mostSigBits = msb;
			this.leastSigBits = lsb;
		}

		/**
		 * 获取类型 4（伪随机生成的）UUID 的静态工厂。 使用加密的本地线程伪随机数生成器生成该 UUID。
		 * 
		 * @return 随机生成的 {@code UUID}
		 */
		public static UUID fastUUID() {
			return randomUUID(false);
		}

		/**
		 * 获取类型 4（伪随机生成的）UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
		 * 
		 * @return 随机生成的 {@code UUID}
		 */
		public static UUID randomUUID() {
			return randomUUID(true);
		}

		/**
		 * 获取类型 4（伪随机生成的）UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
		 * 
		 * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码，否则可以得到更好的性能
		 * @return 随机生成的 {@code UUID}
		 */
		public static UUID randomUUID(boolean isSecure) {
			final Random ng = isSecure ? Holder.numberGenerator : getRandom();

			byte[] randomBytes = new byte[16];
			ng.nextBytes(randomBytes);
			randomBytes[6] &= 0x0f; /* clear version */
			randomBytes[6] |= 0x40; /* set to version 4 */
			randomBytes[8] &= 0x3f; /* clear variant */
			randomBytes[8] |= 0x80; /* set to IETF variant */
			return new UUID(randomBytes);
		}

		/**
		 * 返回此{@code UUID} 的字符串表现形式。
		 *
		 * <p>
		 * UUID 的字符串表示形式由此 BNF 描述：
		 * 
		 * <pre>
		 * {@code
		 * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
		 * time_low               = 4*<hexOctet>
		 * time_mid               = 2*<hexOctet>
		 * time_high_and_version  = 2*<hexOctet>
		 * variant_and_sequence   = 2*<hexOctet>
		 * node                   = 6*<hexOctet>
		 * hexOctet               = <hexDigit><hexDigit>
		 * hexDigit               = [0-9a-fA-F]
		 * }
		 * </pre>
		 * 
		 * </blockquote>
		 *
		 * @return 此{@code UUID} 的字符串表现形式
		 * @see #toString(boolean)
		 */
		@Override
		public String toString() {
			return toString(false);
		}

		/**
		 * 返回此{@code UUID} 的字符串表现形式。
		 *
		 * <p>
		 * UUID 的字符串表示形式由此 BNF 描述：
		 * 
		 * <pre>
		 * {@code
		 * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
		 * time_low               = 4*<hexOctet>
		 * time_mid               = 2*<hexOctet>
		 * time_high_and_version  = 2*<hexOctet>
		 * variant_and_sequence   = 2*<hexOctet>
		 * node                   = 6*<hexOctet>
		 * hexOctet               = <hexDigit><hexDigit>
		 * hexDigit               = [0-9a-fA-F]
		 * }
		 * </pre>
		 * 
		 * </blockquote>
		 *
		 * @param isSimple 是否简单模式，简单模式为不带'-'的UUID字符串
		 * @return 此{@code UUID} 的字符串表现形式
		 */
		public String toString(boolean isSimple) {
			final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36);
			// time_low
			builder.append(digits(mostSigBits >> 32, 8));
			if (false == isSimple) {
				builder.append('-');
			}
			// time_mid
			builder.append(digits(mostSigBits >> 16, 4));
			if (false == isSimple) {
				builder.append('-');
			}
			// time_high_and_version
			builder.append(digits(mostSigBits, 4));
			if (false == isSimple) {
				builder.append('-');
			}
			// variant_and_sequence
			builder.append(digits(leastSigBits >> 48, 4));
			if (false == isSimple) {
				builder.append('-');
			}
			// node
			builder.append(digits(leastSigBits, 12));

			return builder.toString();
		}

		/**
		 * 返回此 UUID 的哈希码。
		 *
		 * @return UUID 的哈希码值。
		 */
		public int hashCode() {
			long hilo = mostSigBits ^ leastSigBits;
			return ((int) (hilo >> 32)) ^ (int) hilo;
		}

		/**
		 * 将此对象与指定对象比较。
		 * <p>
		 * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的
		 * varriant、包含相同的值（每一位均相同）时，结果才为 {@code true}。
		 *
		 * @param obj 要与之比较的对象
		 *
		 * @return 如果对象相同，则返回 {@code true}；否则返回 {@code false}
		 */
		public boolean equals(Object obj) {
			if ((null == obj) || (obj.getClass() != UUID.class)) {
				return false;
			}
			UUID id = (UUID) obj;
			return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits);
		}

		// Comparison Operations

		/**
		 * 将此 UUID 与指定的 UUID 比较。
		 *
		 * <p>
		 * 如果两个 UUID 不同，且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段，则第一个 UUID 大于第二个 UUID。
		 *
		 * @param val 与此 UUID 比较的 UUID
		 *
		 * @return 在此 UUID 小于、等于或大于 val 时，分别返回 -1、0 或 1。
		 *
		 */
		public int compareTo(UUID val) {
			// The ordering is intentionally set up so that the UUIDs
			// can simply be numerically compared as two numbers
			return (this.mostSigBits < val.mostSigBits ? -1 : //
					(this.mostSigBits > val.mostSigBits ? 1 : //
							(this.leastSigBits < val.leastSigBits ? -1 : //
									(this.leastSigBits > val.leastSigBits ? 1 : //
											0))));
		}

		// -------------------------------------------------------------------------------------------------------------------
		// Private method start
		/**
		 * 返回指定数字对应的hex值
		 * 
		 * @param val    值
		 * @param digits 位
		 * @return 值
		 */
		private static String digits(long val, int digits) {
			long hi = 1L << (digits * 4);
			return Long.toHexString(hi | (val & (hi - 1))).substring(1);
		}

		/**
		 * 获取{@link SecureRandom}，类提供加密的强随机数生成器 (RNG)
		 * 
		 * @return {@link SecureRandom}
		 */
		public static SecureRandom getSecureRandom() {
			try {
				return SecureRandom.getInstance("SHA1PRNG");
			} catch (NoSuchAlgorithmException e) {
				throw new RuntimeException(e);
			}
		}

		/**
		 * 获取随机数生成器对象<br>
		 * ThreadLocalRandom是JDK 7之后提供并发产生随机数，能够解决多个线程发生的竞争争夺。
		 * 
		 * @return {@link ThreadLocalRandom}
		 */
		public static ThreadLocalRandom getRandom() {
			return ThreadLocalRandom.current();
		}
	}
}
